package com.tanhua.server.service;

import com.alibaba.fastjson.JSON;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.commons.templates.FaceTemplate;
import com.tanhua.commons.templates.HuanXinTemplate;
import com.tanhua.commons.templates.OssTemplate;
import com.tanhua.commons.templates.SmsTemplate;
import com.tanhua.domain.db.User;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.RecommendUser;
import com.tanhua.domain.vo.*;
import com.tanhua.dubbo.api.UserApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.mongo.FriendApi;
import com.tanhua.dubbo.api.mongo.UserLikeApi;
import com.tanhua.dubbo.api.mongo.VisitorApi;
import com.tanhua.server.interceptors.UserHolder;
import com.tanhua.server.utils.GetAgeUtil;
import com.tanhua.server.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class UserService {

    @Reference
    private UserApi userApi;

    @Reference
    private UserInfoApi userInfoApi;

    @Reference
    private FriendApi friendApi;

    @Reference
    private UserLikeApi userLikeApi;

    @Reference
    private VisitorApi visitorApi;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SmsTemplate smsTemplate;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private FaceTemplate faceTemplate;

    @Autowired
    private OssTemplate ossTemplate;
    
    @Autowired
    private HuanXinTemplate huanXinTemplate;

    @Value("${tanhua.redisValidateCodeKeyPrefix}")
    private String redisValidateCodeKeyPrefix;

    /**
     * 通过手机号码查询
     * @param phone
     * @return
     */
    public User findByMobile(String phone) {
        return userApi.findByMobile(phone);
    }

    /**
     * 添加用户
     * @param user
     */
    public void add(User user) {
        userApi.add(user);
    }

    /**
     * 发送验证码
     * @param paramMap
     */
    public void sendValidateCode(Map<String, String> paramMap) {
        //1. 取出手机号码
        String phone = paramMap.get("phone");
        //2. 拼接验证码的key
        String key = redisValidateCodeKeyPrefix+phone;
        //3. 通过key获取redis中的验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);
        log.info("codeInRedis: {},{}", phone, codeInRedis);
        //4. 存在, 报错：验证码未失效
        if(StringUtils.isNotEmpty(codeInRedis)){
            // 有值
            //System.out.println("报错：验证码未失效");
            throw new TanHuaException(ErrorResult.duplicate());
        }
        //5. 不存在
        //6.    生成验证码
        String validateCode = "123456";//RandomStringUtils.randomNumeric(6);
        //7.    发送验证码
        //Map<String, String> resultMap = smsTemplate.sendValidateCode(phone, validateCode);
        //if(null != resultMap){
        //    //System.out.println("发送验证码失败了" +  resultMap.get(SmsTemplate.SMSRESPONSE_MESSAGE));
        //    log.error("发送验证码失败了! {}, {}", resultMap.get(SmsTemplate.SMSRESPONSE_CODE),resultMap.get(SmsTemplate.SMSRESPONSE_MESSAGE));
        //    throw new TanHuaException(ErrorResult.fail());
        //}
        //8.    存入redis，设置有效期为10分钟。
        stringRedisTemplate.opsForValue().set(key, validateCode, 10, TimeUnit.MINUTES);
    }

    /**
     * 登录注册 - 验证码校验
     * @param paramMap
     * @return
     */
    public Map<String, Object> loginVerification(Map<String, String> paramMap) {
        //1.验证码校验
        //1.1 取出手机号码与验证码
        String phone = paramMap.get("phone");
        // 验证码
        String verificationCode = paramMap.get("verificationCode");
        //1.2 拼接redis中的key
        String key = redisValidateCodeKeyPrefix+phone;
        //1.3 从redis取中验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);
        log.info("验证码校验：{},{},{}", phone, verificationCode, codeInRedis);
        //1.4 redis中的验证码失效，
        if(StringUtils.isEmpty(codeInRedis)){
            //为null，报错：验证码已失效，请重新获取
            throw new TanHuaException(ErrorResult.loginError());
        }
        //1.5 存在，比较前端与redis中的验证码是否相同
        if(!StringUtils.equals(codeInRedis, verificationCode)){
            //1.6 不相同则报错，验证码错误
            throw new TanHuaException(ErrorResult.validateCodeError());
        }
        //1.7 相同，删除redis中的key。防止重复提交，导致频繁的查询数据库
        stringRedisTemplate.delete(key);

        //2.是否已存在用户
        boolean isNew = false;
        //  通过手机号码查询用户信息
        User loginUser = userApi.findByMobile(phone);
        //2.1 不存在则注册为新用户
        if(null == loginUser){
            //注册为新用户
            loginUser = new User();
            loginUser.setMobile(phone);
            // 手机号码后6位，并进行md5加密
            loginUser.setPassword(DigestUtils.md5Hex(phone.substring(5)));
            Long userId = userApi.add(loginUser);
            loginUser.setId(userId);
            // 新注册
            isNew = true;
            // 同步注册到环信上
            huanXinTemplate.register(userId);
        }
        //3. 签发token
        String token = jwtUtils.createJWT(phone, loginUser.getId());
        //4. 把token做为key,用户信息为value存入redis备用，当用户下次再请求时，会带token，从redis中来获取用户信息，如果取不到说明用户没登录，或失效
        //                              存入redis时要设置有效期
        String loginUserJsonString = JSON.toJSONString(loginUser);
        // 7天有效期
        stringRedisTemplate.opsForValue().set("TOKEN_" + token, loginUserJsonString, 7, TimeUnit.DAYS);
        //构建返回Map
        Map<String,Object> resultMap = new HashMap<String,Object>();
        resultMap.put("token",token);
        resultMap.put("isNew",isNew);
        return resultMap;
    }

    /**
     * 完善个人信息
     * @param userInfoVo
     */
    public void loginReginfo(UserInfoVo userInfoVo) {
        //1. 通过token获取登录用户的id，同时还可以校验用户是否有效
        User loginUser = UserHolder.getUser();
        //2. 添加个人信息,保存tb_user_info userInfoApi
        //2.1 把userInfoVo转成实体类userInfo
        UserInfo userInfo = new UserInfo();
        // 复制属性, 把p1的属性值复制到p2里去，要求，属性名与类型必须一致才会复制
        //p1: 要复制的源
        //p2: 复制到的目标
        BeanUtils.copyProperties(userInfoVo,userInfo);
        //2.2 设置登录的id
        userInfo.setId(loginUser.getId());
        // 设置年龄
        userInfo.setAge(GetAgeUtil.getAge(userInfoVo.getBirthday()));
        //2.3 调用api保存用户信息
        userInfoApi.add(userInfo);
    }


    /**
     * 完美个人信息中-上传头像
     * @param headPhoto
     */
    public void uploadAvatar(MultipartFile headPhoto) {
        //1. 从token获取登录用户id, 校验token是否有效
        User loginUser = UserHolder.getUser();
        //2. 调用百度人脸检测
        try {
            if (!faceTemplate.detect(headPhoto.getBytes())) {
                //3. 不通过则报错
                throw new TanHuaException(ErrorResult.faceError());
            }
            //4. 通过，则要上传到oss存储,得到头像的地址
            String avatarUrl = ossTemplate.upload(headPhoto.getOriginalFilename(), headPhoto.getInputStream());
            //5. 构建pojo，更新
            UserInfo pojo = new UserInfo();
            pojo.setAvatar(avatarUrl);
            pojo.setId(loginUser.getId());
            //6. 调用api更新头像
            userInfoApi.update(pojo);
        } catch (IOException e) {
            log.error("上传头像失败.",e);
            throw new TanHuaException("上传头像失败");
        }

    }

    /**
     * 查询用户信息
     * @param userID
     * @param huanxinID
     * @return
     */
    public UserInfoVo getUserInfo(Long userID, Long huanxinID) {
        //1. 获取登录用户id,校验token是否有效
        User loginUser = UserHolder.getUser();
        //2. 选择要查询的id
        Long userId = loginUser.getId();
        if(null != userID){
            userId = userID;
        }
        if(null != huanxinID){
            userId = huanxinID;
        }
        //3. 调用api查询用户信息
        UserInfo userInfo = userInfoApi.findById(userId);
        //4. 转成vo返回
        UserInfoVo vo = new UserInfoVo();
        // 复制属性
        BeanUtils.copyProperties(userInfo,vo);
        // 年龄
        vo.setAge(userInfo.getAge().toString());
        return vo;
    }

    /**
     * 更新登录用户信息
     * @param vo
     */
    public void updateUserInfo(UserInfoVo vo) {
        //1. 获取登录用户的id
        Long loginUserId = UserHolder.getUserId();
        //2. 把vo转成pojo
        UserInfo pojo = new UserInfo();
        BeanUtils.copyProperties(vo, pojo);
        //3. 计算年龄
        pojo.setAge(GetAgeUtil.getAge(vo.getBirthday()));
        //4. 设置用户的id
        pojo.setId(loginUserId);
        //5. 调用api更新
        userInfoApi.update(pojo);
    }

    /**
     * 互相喜欢、喜欢、粉丝统计
     * @return
     */
    public CountsVo counts() {
        //1. 统计相互喜欢
        long eachLoveCount = friendApi.countByUserId(UserHolder.getUserId());
        //2. 统计喜欢个数
        long loveCount = userLikeApi.loveCount(UserHolder.getUserId());
        //3. 统计粉丝个数
        long fanCount= userLikeApi.fansCount(UserHolder.getUserId());
        //构建countsVo返回
        CountsVo vo = new CountsVo();
        vo.setFanCount(fanCount);
        vo.setLoveCount(loveCount);
        vo.setEachLoveCount(eachLoveCount);
        return vo;
    }

    /**
     * 互相喜欢、喜欢、粉丝、谁看过我 - 翻页列表
     * @param type 1 互相关注
     *             2 我关注
     *             3 粉丝
     *             4 谁看过我
     * @param page
     * @param pageSize
     * @param nickname
     * @return
     */
    public PageResult<FriendVo> queryUserLikeList(int type, Long page, Long pageSize, String nickname) {
        //1.通过不同的type查询数据，使用switch
        PageResult pageResult = new PageResult();
        switch (type){
            //1.1 分页查询好友
            case 1: pageResult = friendApi.findPageWithScore(UserHolder.getUserId(), page, pageSize);break;
            //1.2 查询我喜欢的佳人
            case 2: pageResult = userLikeApi.findPageMyLove(UserHolder.getUserId(), page, pageSize);break;
            //1.3 查询我的粉丝
            case 3: pageResult = userLikeApi.findPageMyFans(UserHolder.getUserId(), page, pageSize);break;
            //1.4 查询谁看过我
            case 4: pageResult = visitorApi.findPageByUserId(UserHolder.getUserId(), page, pageSize);break;
            default:break;
        }
        //2. 查询这些用户的信息
        List<RecommendUser> recommendUserList = pageResult.getItems();
        List<FriendVo> voList = new ArrayList<>();
        //2.0 对查询到的结果集非空判断
        if(!CollectionUtils.isEmpty(recommendUserList)) {
            //2.1 取出所有用户的id
            List<Long> userIds = recommendUserList.stream().map(RecommendUser::getUserId).collect(Collectors.toList());
            //2.2 批量查询用户信息
            List<UserInfo> userInfoList = userInfoApi.findByBatchId(userIds);
            //2.3 转成map
            Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, u -> u));
            //3. 转成vo
            //3.1 以分页的结果遍历
            for (RecommendUser recommendUser : recommendUserList) {
                //3.2 构建vo
                FriendVo vo = new FriendVo();
                // 获取用户的信息
                UserInfo userInfo = userInfoMap.get(recommendUser.getUserId());
                BeanUtils.copyProperties(userInfo, vo);
                // 缘分值
                vo.setMatchRate(recommendUser.getScore().intValue());
                voList.add(vo);
            }
        }
        //3.3 设置回pageResult
        pageResult.setItems(voList);
        //4. 返回
        return pageResult;
    }
}
